﻿namespace DataCapture.Helper;

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;

public static class DictionaryMapper<T> where T : class, new()
{
    private static readonly Func<Dictionary<string, object>, T> _mapperFunc = BuildMapper();

    // 自定义字段别名（可选）
    private static readonly Dictionary<string, string> _customMappings = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);

    public static void AddMapping(string dictionaryKey, string propertyName)
    {
        _customMappings[dictionaryKey] = propertyName;
    }

    public static void ClearMappings()
    {
        _customMappings.Clear();
    }

    private static Func<Dictionary<string, object>, T> BuildMapper()
    {
        var dictType = typeof(Dictionary<string, object>);
        var itemParam = Expression.Parameter(dictType, "dict");

        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => p.CanWrite);

        var bindings = new List<MemberBinding>();

        foreach (var prop in properties)
        {
            string propName = prop.Name;

            // 查找是否有自定义映射
            if (_customMappings.TryGetValue(propName, out var customKey))
                propName = customKey;

            var keyExpr = Expression.Constant(propName);
            var valueVar = Expression.Variable(typeof(object), "value");

            // 构造 TryGetValue 表达式
            var tryGetValueCall = Expression.Call(
                itemParam,
                typeof(Dictionary<string, object>).GetMethod("TryGetValue", new[] { typeof(string), typeof(object).MakeByRefType() }),
                keyExpr,
                valueVar
            );

            var condition = Expression.Condition(
                Expression.AndAlso(
                    Expression.Call(itemParam, typeof(Dictionary<string, object>).GetMethod("ContainsKey", new[] { typeof(string) }), keyExpr),
                    Expression.NotEqual(valueVar, Expression.Constant(null))
                ),
                ConvertValueExpression(valueVar, prop.PropertyType),
                Expression.Default(prop.PropertyType)
            );

            var binding = Expression.Bind(prop, condition);
            bindings.Add(binding);
        }

        var body = Expression.MemberInit(Expression.New(typeof(T)), bindings);
        var lambda = Expression.Lambda<Func<Dictionary<string, object>, T>>(body, itemParam);
        return lambda.Compile();
    }

    // 处理不同类型转换（包括嵌套对象）
    private static Expression ConvertValueExpression(Expression valueExp, Type targetType)
    {
        // 如果是基本类型或字符串，直接转换
        if (targetType.IsPrimitive || targetType == typeof(string) || targetType == typeof(object))
        {
            return Expression.Convert(
                Expression.Property(valueExp, "ToString"),
                targetType
            );
        }

        // 如果是另一个字典支持的实体类，递归调用 Mapper
        if (typeof(T).Assembly.GetTypes().Contains(targetType) &&
            targetType.IsClass && targetType != typeof(string))
        {
            var method = typeof(DictionaryMapper<>).MakeGenericType(targetType)
                .GetMethod("Map", new[] { typeof(Dictionary<string, object>) });

            var parameterExp = Expression.Parameter(typeof(object), "value");
            var castedValue = Expression.Convert(parameterExp, typeof(Dictionary<string, object>));

            var callExp = Expression.Call(null, method, castedValue);

            return Expression.Convert(
                Expression.Invoke(Expression.Lambda(castedValue, parameterExp), valueExp),
                targetType
            );
        }

        // 默认尝试使用 Convert.ChangeType
        return Expression.Convert(
            Expression.Call(null, typeof(Convert).GetMethod("ChangeType", new[] { typeof(object), typeof(Type) }), valueExp, Expression.Constant(targetType)),
            targetType
        );
    }

    public static T Map(Dictionary<string, object> dict)
    {
        return _mapperFunc(dict);
    }
}